

import java.io.IOException;
import java.util.LinkedList;


public class GameState {
	private LinkedList<Player> players;
	private int maxPlayers;
	
	private Deck myDeck;
	private Player currPlayer;
	private Player dealer;
	private LinkedList<Card> communityCards;
	private Player better;
	private int pot;
	private int currBet;
	private boolean handOver;
	private int currRound; //0 = preflop, 1 = flop, 2 = river, 3 = turn
	private int pCount;
	private int foldCount;
	private LinkedList<Action> actionLog;
	private int handCount;
	
	public GameState()
	{
		maxPlayers = 8;
		players = new LinkedList<Player>();
		communityCards = new LinkedList<Card>();	
		myDeck = new Deck();
		handOver = false;
		foldCount = 0;
		pCount = 0;
		actionLog = new LinkedList<Action>();
		handCount = 0;
	}
	
	public void addPlayer(Player p)
	{
		pCount++;
		if(players.size() < maxPlayers)
			players.add(p);
	}
	public LinkedList<Card> getCommCards(){
		return communityCards;
	}
	public void addToPot(int toAdd)
	{
		pot += toAdd;
	}
	public void clearActionLog()
	{
		for(int i=0; i<actionLog.size(); i++)
		{
			actionLog.remove();
		}
	}
	public int getCurrRound()
	{
		return currRound;
	}
	public int getNumPlayers()
	{
		return players.size();
	}
	public LinkedList<Player> getPlayers()
	{
		return players;
	}
	private void bet()
	{
		System.out.println("BET!");
		int cBet=0;
		cBet = currPlayer.getBet();
		addToPot( cBet );
		setCurrBet( cBet );
		better = currPlayer;
		actionLog.add(new Action(currPlayer, "bet", cBet));
		currPlayer = getNextPlayer(currPlayer);
	}
	private void check()
	{
		System.out.println("CHECK!");
		actionLog.add(new Action(currPlayer, "check"));
		currPlayer = getNextPlayer(currPlayer);
	}
	public void dealCard()
	{
		int currPlayerIndex = players.indexOf(currPlayer);
		do
		{
			currPlayer.receiveCard(myDeck.getNextCard());
			currPlayer = getNextPlayer(currPlayer);
		}
		while(players.indexOf(currPlayer) != currPlayerIndex);	
		// System.out.println("Finished while looooooooooooooooooooooooooooooooooooop");
	}
	public void dealFlop(){
		// flop is placed on the table, but burn one first
		myDeck.getNextCard();
		communityCards.add(myDeck.getNextCard());
		communityCards.add(myDeck.getNextCard());
		communityCards.add(myDeck.getNextCard());
		System.out.println("FLOP DEALT");
		logCommunityCards();
	}
	public void dealRiver(){
		// river is placed on table, but burn first
		myDeck.getNextCard();
		System.out.println("RIVER DEALT");
		communityCards.add(myDeck.getNextCard());
		logCommunityCards();
	}
	public void dealTurn(){
		// turn is placed on table, but burn one first
		myDeck.getNextCard();
		System.out.println("TURN DEALT");
		communityCards.add(myDeck.getNextCard());
		logCommunityCards();
	}
	private void fold()
	{
		System.out.println("FOLD!");
		currPlayer.fold();
		if(better == currPlayer)
		{
			better = getNextPlayer(currPlayer);
		}
		foldCount++;	/////////////// vincent make sure fold count goes up first bc get next player needs that info
		actionLog.add(new Action(currPlayer, "fold"));
		currPlayer = getNextPlayer(currPlayer);
	}
	
	public int getCurrBet()
	{
		return currBet;
	}
	public Player getCurrentPlayer(){
		return currPlayer;
	}
	private Player getNextPlayer(Player p)
	{
		int currPlayerIndex = players.indexOf(p);
		Player toReturn;
		//System.out.println("pCount is: " + pCount + "and foldCount is" + foldCount);
		if(pCount - foldCount == 1)
		{
			//System.out.println("Hand ennnnnnnnnnnnnnnded");
		
			handOver = true;
			return p;			
		}
		if(currPlayerIndex < players.size()-1)
		{
			toReturn = players.get(currPlayerIndex+1);
			//System.out.println("    " + players.get(currPlayerIndex+1).getName());
			if(toReturn.getHasFolded())
			{
				//System.out.println(" reeeeeeeeeeeeeeeecursion " + toReturn.getName());
				
				currPlayer = players.get(currPlayerIndex+1);	////////// vincent added to fix loop (used to just send same person (one after curr
				return (getNextPlayer(toReturn));	
			}
		}
		else
		{
			toReturn = players.get(0);
			if(toReturn.getHasFolded())
			{
				return getNextPlayer(toReturn);
			}
		}
		return toReturn;		
	}
	public int getPot()
	{
		return pot;
	}
	private String[] getValidActions()
	{
		String[] toReturn = new String[3];
		if(currBet == 0)
		{
			toReturn[0] = "bet";
			toReturn[1] = "check";
			toReturn[2] = "fold";
		}
		else
		{
			toReturn[0] = "raise";
			toReturn[1] = "call";
			toReturn[2] = "fold";
		}
		return toReturn;
	}
	private boolean handOver()
	{
		if(currRound > 3)
			return true;
		return handOver;
	}
	public void init()
	{
		addPlayer(new Player("Human"));
		addPlayer(new AIPlayer("AI: Snooki", this));
		addPlayer(new AIPlayer("AI: Paulie", this));
		addPlayer(new AIPlayer("AI: Mike", this));
		
	}
	public void initHand()
	{
		/////////////////////// vincent loop added to reset folders for each new hand  
		///// the line below wiht getNextPlayer(when all the players have folded and not been resets create infinte loop
		handOver = false;	// vincent handover was never eset back to false
		int j=0;
		foldCount = 0;
		currBet = 0;
		for(j = 0; players.get(j) != players.getLast(); j++){
			players.get(j).setFolded(false);
			players.get(j).setNotActed();
		}
		players.get(j).setFolded(false);
		players.get(j).setNotActed();
		System.out.println("*-*-*-*-*NEW HAND STARTED*-*-*-*-*");
		myDeck.shuffleDeck();
		if(dealer == null)
		{
			dealer = players.get(0);
		}
		currPlayer = getNextPlayer(dealer);
		//System.out.println("Dealer is doneeeeeeeeeeeeeeeeee");
		communityCards.clear();
		dealCard();
		dealCard();	
		currRound = 0;
	}
	public LinkedList<Action> getActionLog()
	{
		return actionLog;
	}
	public void incrementRound()
	{
		currRound++;
	}
	public void initRound()
	{
		System.out.println("=-=-New round start!-=-=");
		handOver = false;   ////////////////////////////// vincent added to reset handover 
		currPlayer = getNextPlayer(dealer);
		currBet = 0;
		better = currPlayer;
		for(int i=0; i<players.size(); i++)
		{
			players.get(i).setRaised(false);
			players.get(i).setNotActed();
			players.get(i).calcPossibleHands(communityCards);
		}
		if(currRound == 1)
		{
			dealFlop();
		}
		else if(currRound == 2)
		{
			dealTurn();
		}
		else if(currRound == 3)
		{
			dealRiver();
		}
		else if(currRound == 4)
		{
			showdown();
			
		}
		
	}
	private void showdown()
	{
		System.out.println("\n\n\n*^*_*^*_*^*_*^*_*^*_*^*_*^*_*^*_*^*_*^*_*");
		System.out.println("SHOWDOWN");
		this.logCommunityCards();
		for(int i=0; i<players.size(); i++)
		{
			System.out.println("Player " + players.get(i).getName() + " has...");
			System.out.print(players.get(i).toStringHoleCards());
		}				
		//Player winner = findWinner();
		//System.out.println("Winning player is... " + winner.getName());
		//System.out.println("Winnings: " + pot);
		System.out.println("*^*_*^*_*^*_*^*_*^*_*^*_*^*_*^*_*^*_*^*_*\n\n\n");
		//winner.receiveMoney(pot);
		//pot = 0;
		handOver = true;
	}
	private Player findWinner()
	{
		Player winner = new Player();
		for(int i=0; i<players.size(); i++)
		{
			if(winner.getName().equals("dne"))		// vincent cant say winner == null
			{
				if(!players.get(i).getHasFolded())
				{
					winner = players.get(i);
				}
			}
			else
			{
				if(!players.get(i).getHasFolded())
				{
					if(winner.getCurrentBestHand().compareTo(players.get(i).getCurrentBestHand()) < 1)
					{
						winner = players.get(i);
					}
				}
			}
		}
		return winner;
	}
	public void logCommunityCards()
	{
		System.out.println(" \nCommunity Cards \n------------------");
		for(int i=0; i<communityCards.size(); i++)
		{
			System.out.println(communityCards.get(i).toString());
		}
		System.out.println();
	}
	public void logHoleCards()
	{
		System.out.println(" \n");
		for(int i=0; i<players.size(); i++)
		{
			System.out.println("Player " + (i+1) + " \n----------------\n" + players.get(i).toStringHoleCards());
		}
	}
	public boolean playRound() throws IOException
	{
		String[] validActions;
		if(handCount == 10000)
		{
			for(int i=0; i<players.size();i++)
			{
				((AIPlayer)players.get(i)).printOppInfo();
			}
			System.exit(0);
		}
		while(!roundOver())
		{
			
			///////////////////////////// vincent added this if statement
			//// if handover became true because everyone (except one) folded then handover was true
			/// but we never actualy reset the hand (and fold count), thus when the fold count kept incrementing then get next player is above pcount - foldcount
			//  thus it keeps seeing that someone has folded and calling itself again recursively loopng forever
			// , with this if statement it forces us to reset the hand and fold count 
			if(handOver == true)
			{
				finishHand();
				return false;
			}
			
			validActions = getValidActions();
			processAction(currPlayer.act(validActions, currBet));
			System.out.println("Pot = " + pot);
		}
		if(handOver())
		{
			finishHand();
			return false;
		}
		return true;
	}
	private void finishHand()
	{
		Player winner = findWinner();
		System.out.println("\n\n****Winner is... " + winner.getName() + " ****");
		System.out.println("****Winnings : " + pot +" ****");
		winner.receiveMoney(pot);
		pot = 0;
		handCount++;
		for(int i=0; i<players.size(); i++)
		{
			if(players.get(i).type == "AI")
			{
				((AIPlayer)players.get(i)).playerWon(winner);
			}
			players.get(i).clearHoleCards();
		}		
	}
	private void processAction(String action)
	{
		if(action == "bet") {
			bet();
		}
		if(action == "raise"){
			raise();
		}
		if(action == "fold")
			fold();
		if(action == "check")
			check();
		if(action == "call")
			call();
	}
	private void call()
	{
		System.out.println("CALL!");
		int cBet=0;
		cBet = currPlayer.getBet();
		addToPot( cBet );
		setCurrBet( cBet );
		actionLog.add(new Action(currPlayer, "call", cBet));
		currPlayer = getNextPlayer(currPlayer);
	}
	private void raise()
	{
		System.out.println("RAISE!");
		int cBet=0;
		cBet = currPlayer.getBet();
		addToPot( cBet );
		setCurrBet( cBet );
		better = currPlayer;
		actionLog.add(new Action(currPlayer, "raise", cBet));
		currPlayer = getNextPlayer(currPlayer);
	}
	public void removePlayer(Player p)
	{
		players.remove(p);
	}
	public boolean roundOver()
	{
		if(currPlayer != null && better != null)
		{
			if(currPlayer == better && currPlayer.hasActed())
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		return false;
	}
	public void setCurrBet(int amt)
	{
		currBet = amt;
	}
	
}
